﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.Optimization
{
    /// <summary>
    /// This class provides the searching of a minimum by using the conjugate gradient algorithm.
    /// </summary>
    [Serializable]
    public class ConjugateGradientOptimizer : AbstractSimpleOptimizer
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ConjugateGradientOptimizer"/> class.
        /// </summary>
        /// <param name="sourcePolynomial">The polynomial from which a minimum should be found.</param>
        public ConjugateGradientOptimizer(Polynomial sourcePolynomial)
            : base(sourcePolynomial)
        {
        }

        /// <summary>
        /// Finds a minimum of the specified polynomial.
        /// </summary>
        /// <param name="approximationValue">The approximation value of the minimum.</param>
        /// <returns>
        /// The value x of the specified function f(x), where the algorithm finds a minimum.
        /// </returns>
        public double FindMinimum(double approximationValue)
        {
            return this.FindMinimum(approximationValue, 1000, 1e-15);
        }

        /// <summary>
        /// Finds a minimum of the specified polynomial.
        /// </summary>
        /// <param name="approximationValue">The approximation value of the minimum.</param>
        /// <param name="iterations">The maximum numbers of iterations to find a minimum.</param>
        /// <returns>
        /// The value x of the specified function f(x), where the algorithm finds a minimum.
        /// </returns>
        public double FindMinimum(double approximationValue, int iterations)
        {
            return this.FindMinimum(approximationValue, iterations, 1e-15);
        }

        /// <summary>
        /// Finds a minimum of the specified polynomial.
        /// </summary>
        /// <param name="approximationValue">The approximation value of the minimum.</param>
        /// <param name="iterations">The maximum numbers of iterations to find a minimum.</param>
        /// <param name="precision">The precision of the result.</param>
        /// <returns>
        /// The value x of the specified function f(x), where the algorithm finds a minimum.
        /// </returns>
        public double FindMinimum(double approximationValue, int iterations, double precision)
        {
            int i = 0;
            double oldValue = 0;
            double newValue = approximationValue;
            const double stepSize = 0.01;
            Polynomial workingPolynomial = this.SourcePolynomial.Derivative();

            while (i < iterations)
            {
                if (Math.Abs(newValue - oldValue) < precision)
                {
                    this.neededIterations = i;
                    this.precisionError = false;
                    this.relativeError = Math.Abs(newValue - oldValue);

                    return newValue;
                }

                oldValue = newValue;
                newValue = newValue - stepSize * workingPolynomial.SolveAt(newValue);
                i++;
            }

            this.neededIterations = iterations;
            this.precisionError = true;
            this.relativeError = Math.Abs(newValue - oldValue);

            return newValue;
        }
    }
}